<?php


namespace paper\view;

use http\Exception\RuntimeException;
use paper\view\driver\Parse;

class View
{

    private $vars = [];
    private $compilePath;


    private array $options = [
        'prefix_path'  => PAPER_ROOT_PATH . DIRECTORY_SEPARATOR.PAPER_APP,
        'compile_path' => DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . 'tpl_cache' . DIRECTORY_SEPARATOR,
        'tag_block'    => ['{%', '%}'],
        'tag_var'      => ['{$', '}'],
        'tag_function' => ['{:', '}'],
        'tag_comment'  => ['{*', "*}"]
    ];

    function __construct()
    {

    }


    /**
     * @param $template
     * @param array $vars
     * @param bool $compile
     */
    public function load($template, array $vars = [], bool $compile = true)
    {
        $sourcePath = $this->parseTemplatePath($template);
        if (!$compile) {
            $this->compilePath = $sourcePath;
        } else {

            $this->compilePath = $this->parseCachePath($template);
            if (!$this->checkCache()) {
                $parse   = new Parse($this->options, $sourcePath);
                $compile = $parse->compile();
                $time    = time();
                //如果使用了模板继承则将此文件加入监听
                if ($extend = $parse->getExtend()) {
                    $listen[$extend] = $time;
                }

                $listen[$sourcePath] = $time;

                $cacheInfo = serialize($listen);
                $head      = '<?php /*' . $cacheInfo . '*/' . PHP_EOL . 'if(!defined(\'PAPER_APP\')){die(\'error!this is a cache file!\');}?>';
                $footer    = '<?php /* compiled at : ' . date('Y/m/d H:i:s', time()) . ' */ ?>';
                if (!file_put_contents($this->compilePath, $head . $compile . $footer)) {
                    throw new RuntimeException($this->compilePath . ":Cache write failed!");
                }
            }
        }

        //执行编译后的文件s
        //单独放置一个函数 是为了不让控制器的变量和内容的变量冲突
        $this->loadCompileFile($vars);
    }


    private function parseTemplatePath($template)
    {
        return $template;
    }

    private function parseCachePath($template): string
    {
        $template = md5($template);
        return $this->options['prefix_path'] . $this->options['compile_path'] . $template . '.php';
    }

    /**
     * @param $var
     * @return string
     * @noinspection PhpUnused
     */
    public function filter($var): string
    {
        return htmlspecialchars($var, ENT_QUOTES);
    }


    private function checkCache(): bool
    {
        if (!file_exists($this->compilePath)) {
            return false;
        }
        // 读取缓存文件失败
        if (!$handle = fopen($this->compilePath, "r")) {
            return false;
        }
        $line = fgets($handle);
        fclose($handle);
        // 读取第一行
        if (!preg_match('/\/\*([^\*]+)\*\//', $line, $matches)) {
            return false;
        }
        $includeFile = unserialize($matches[1]);
        if (!is_array($includeFile)) {
            return false;
        }

        // 检查模板文件是否有更新
        foreach ($includeFile as $path => $time) {
            if (!is_file($path) || filemtime($path) > $time) {
                // 模板文件如果有更新则缓存需要更新
                return false;
            }
        }
        return true;
    }

    /**
     * @param array $vars
     */
    private function loadCompileFile(array $vars = [])
    {
        extract($vars, EXTR_REFS);
        if (function_exists('opcache_invalidate')) {
            opcache_invalidate($this->compilePath, true);
        }
        include($this->compilePath);
    }



    public function import($var)
    {
        $this->vars = array_merge($this->vars, $var);
    }


//    public function __construct(Engine $engine)
//    {
//        $this->engine = $engine;
//    }

    /**
     * @param $var
     * @param $value
     */
    public function assign($var, $value)
    {
        $this->vars[$var] = $value;
    }


//    public function import($var = [])
//    {
//        if ($var)
//            $this->engine->import($var);
//    }

    /**
     * 渲染模板并且返回字符串
     * 在paper\http\response\ViewResponse中调用了此方法
     * @param $template
     * @param array $vars
     * @param bool $compile
     * @return string
     * @noinspection PhpUnused
     */
    public function fetch($template, array &$vars = [], bool $compile = true): string
    {

        ob_start(); //开启缓冲
        ob_implicit_flush(0);

        $this->load($template, $vars, $compile); //运行模板

        return ob_get_clean();
    }

    /**
     * 显示模板内容，直接显示，不缓冲
     * @param $template
     * @param array $vars
     * @param bool $compile
     */
    public function display($template, array $vars = [], bool $compile = true)
    {
        $this->load($template,$vars,$compile);
    }

}